package com.atguigu.tingshu.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.HttpClient;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.order.client.OrderInfoFeignClient;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.vo.account.RechargeInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Value("${appid}")
    private String appid;
    @Value("${partner}")
    private String partner;
    @Value("${partnerkey}")
    private String partnerkey;
    @Value("${notifyUrl}")
    private String notifyUrl;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 获取WeChatPay申请支付的url信息
     *
     * @param params
     * @return
     */
    @Override
    @SneakyThrows
    public String getPayInfoFromWx(Map<String,String> params) {
        // 请求地址
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        // 包装参数
        Map<String, String> paramMap = new HashMap<>();
        // 包装参数 -- 参考官方文档 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
        paramMap.put("appid", appid);
        paramMap.put("mch_id", partner);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("body", params.get("description"));
        paramMap.put("out_trade_no", params.get("orderNo"));
        paramMap.put("total_fee", new BigDecimal(params.get("price")).multiply(new BigDecimal(100)).intValue() + "");
        paramMap.put("spbill_create_ip", "192.168.200.1");
        paramMap.put("notify_url", notifyUrl);
        paramMap.put("trade_type", "NATIVE");
        // 保存attach数据 交换机+routingKey
        Map<String,String> attachMap = new HashMap<>();
        attachMap.put("exchange",params.get("exchange"));
        attachMap.put("routingKey",params.get("routingKey"));
        // 包装attach参数
        paramMap.put("attach",JSONObject.toJSONString(attachMap));
        // 转xml数据并生成签名
        String xmlParamsString =
                WXPayUtil.generateSignedXml(paramMap, partnerkey);
        // 创建HttpClient对象 发起请求
        HttpClient httpClient = new HttpClient(url);
        // https://api.mch.weixin.qq.com/pay/unifiedorder
        httpClient.setHttps(true);
        // xml类型参数
        httpClient.setXmlParam(xmlParamsString);
        // post方法
        httpClient.post();
        // 解析结果 -- xml类型
        String contentXmlString = httpClient.getContent();
        // 将结果转换为map
        Map<String, String> result = WXPayUtil.xmlToMap(contentXmlString);
        // 判断结果 return_code 是否等于 SUCCESS && result_code 是否等于 SUCCESS
        if (result.get("return_code").equals("SUCCESS") &&
                result.get("result_code").equals("SUCCESS")) {
            // 记录本次交易流水数据 -- 异步 本地线程中的userId需要另外传输
            Long userId = AuthContextHolder.getUserId();
            CompletableFuture.runAsync(() -> {
                savePaymentInfo(params, userId);
            });
            // 返回 result_url
            return result.get("code_url");
        }
        // 若无正常返回url则记录错误日志 返回全部内容
        log.error("订单(" + params.get("orderNo") + ")申请支付url失败,失败原因: " + JSONObject.toJSONString(result));
        return null;
    }


    /**
     * 保存交易流水数据
     *
     * @param params
     * @param userId
     */
    public void savePaymentInfo(Map<String,String> params, Long userId) {
        // 初始化
        PaymentInfo paymentInfo = new PaymentInfo();
        // 设置属性
        paymentInfo.setUserId(userId);
        paymentInfo.setPaymentType(params.get("paymentType"));
        paymentInfo.setOrderNo(params.get("orderNo"));
        paymentInfo.setPayWay("1101");
        paymentInfo.setAmount(new BigDecimal(params.get("price")));
        paymentInfo.setContent(params.get("description"));
        paymentInfo.setPaymentStatus("1401");
        // 保存
        paymentInfoService.save(paymentInfo);
    }

    /**
     * 主动获取订单支付结果: 供商户调用 兜底方法
     *
     * @param orderNo
     * @return
     */
    @Override
    @SneakyThrows
    public String getOrderPayResult(String orderNo) {
        // 请求地址
        String url = "https://api.mch.weixin.qq.com/pay/orderquery";
        // 包装参数
        Map<String, String> paramMap = new HashMap<>();
        // 包装参数 -- 参考官方文档 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
        paramMap.put("appid", appid);
        paramMap.put("mch_id", partner);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("out_trade_no", orderNo);
        // 转xml数据并生成签名
        String xmlParamsString =
                WXPayUtil.generateSignedXml(paramMap, partnerkey);
        // 创建HttpClient对象 发起请求 https://api.mch.weixin.qq.com/pay/orderquery
        HttpClient httpClient = new HttpClient(url);
        httpClient.setHttps(true);
        // xml类型参数
        httpClient.setXmlParam(xmlParamsString);
        // post方法
        httpClient.post();
        // 解析结果 -- xml类型
        String contentXmlString = httpClient.getContent();
        // 将结果转换为map
        Map<String, String> result = WXPayUtil.xmlToMap(contentXmlString);
        // 判断结果 return_code 是否等于 SUCCESS && result_code 是否等于 SUCCESS
        if (result.get("return_code").equals("SUCCESS") &&
                result.get("result_code").equals("SUCCESS") &&
                result.get("trade_state").equals("SUCCESS")) {
            // 修改数据库中的未支付订单流水为已支付
            PaymentInfo paymentInfo = paymentInfoService.getOne(
                    new LambdaQueryWrapper<PaymentInfo>()
                            .eq(PaymentInfo::getOrderNo, orderNo)
                            .eq(PaymentInfo::getPaymentStatus, "1401"));
            // 判断
            if (paymentInfo != null) {
                paymentInfo.setPaymentStatus("1402");
                paymentInfo.setCallbackTime(new Date());
                paymentInfo.setCallbackContent(JSONObject.toJSONString(result));
                // 后续处理
                notifyService(result);
                // 更新
                paymentInfoService.updateById(paymentInfo);
            }
            // 返回 交易状态
            return result.get("trade_state");
        }
        // 若无正常返回则记返回全部内容
        return JSONObject.toJSONString(result);
    }

    /**
     * 根据微信返回的支付结果,修改支付的流水数据
     * 被动获取
     * @param result
     */
    @Override
    public void updatePaymentInfo(Map<String, String> result) {
        // 获取orderNo
        String orderNo = result.get("out_trade_no");
        // 获取锁
        RLock lock = redissonClient.getLock("PaymentInfo_Lock_" + orderNo);
        // 对所有通知都进行校验处理
        lock.lock();
        try {
            // 查询当前订单流水记录
            PaymentInfo paymentInfo = paymentInfoService.getOne(
                    new LambdaQueryWrapper<PaymentInfo>()
                            .eq(PaymentInfo::getOrderNo, orderNo));
            // 获取当前支付的流水号
            String outTradeNo = result.get("transaction_id");
            // 获取当前订单支付状态
            if (paymentInfo.getPaymentStatus().equals("1401")) { // 未支付
                // 包装属性
                paymentInfo.setOutTradeNo(outTradeNo);
                paymentInfo.setCallbackContent(JSONObject.toJSONString(result));
                paymentInfo.setCallbackTime(new Date());
                paymentInfo.setPaymentStatus("1402");
                paymentInfoService.updateById(paymentInfo);
                // 根据返回结果的类型选择后续操作
                notifyService(result);
            } else {
                // 已支付 获取上一次的流水号
                if (!paymentInfo.getOutTradeNo().equals(outTradeNo)) {
                    // 两次流水号不同 --> 不同渠道付款
                    System.out.println("退款操作");
                }
            }
        } catch (Exception e) {
            // TODO-将错误信息记录到数据库
        } finally {
            lock.unlock();
        }


    }

    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 根据流水的类型通知对应的微服务执行对应操作
     * @param result
     */
    private void notifyService(Map<String,String> result) {
        // 获取附加数据
        String attachString = result.get("attach");
        // 反序列化
        Map<String,String> attachMap = JSONObject.parseObject(attachString, Map.class);
        // 获取 exchange 和  routingKey
        String exchange = attachMap.get("exchange");
        String routingKey = attachMap.get("routingKey");
        // 消费者发消息
        rabbitTemplate.convertAndSend(
                exchange,
                routingKey,
                JSONObject.toJSONString(result));
    }

}
